import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, LabelEncoder
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import LabelEncoder
import numpy as np

# 加载本地字体并设置为全局字体
font_path = r'c:\windows\fonts\STZHONGS.TTF'
my_font = fm.FontProperties(fname=font_path)
plt.rcParams['font.family'] = my_font.get_name()
plt.rcParams['axes.unicode_minus'] = False

# 加载数据集
file_path = r'G:\two\partens.xlsx'
data = pd.read_excel(file_path)

# 设置 Pandas 的显示选项
pd.set_option('display.max_rows', None)  # 显示所有行
pd.set_option('display.max_columns', None)  # 显示所有列
pd.set_option('display.width', None)  # 自动调整宽度

# 数据的初步概览和可视化展示
print("数据集的前几行：\n", data.head())
print("数据集的描述性统计：\n", data.describe())
print("数据集的列名：\n", data.columns)
print('数据集的大小为',data.shape)

# 输出每个特征的唯一属性取值
for column in data.columns:
    unique_values = data[column].unique()
    print(f"{column} 的唯一取值: {unique_values}")

# 检查缺失值
print("缺失值统计：\n", data.isnull().sum())

# 可视化缺失值
plt.figure(figsize=(10, 6))
sns.heatmap(data.isnull(), cbar=False, cmap='viridis')
plt.title('缺失值热图', fontproperties=my_font)
plt.show()

# 编码分类特征并存储到新数据框
encoded_data = data.copy()
label_encoders = {}
for column in encoded_data.select_dtypes(include=[object]).columns:
    le = LabelEncoder()
    encoded_data[column] = le.fit_transform(encoded_data[column])
    label_encoders[column] = le

# 使用 Isolation Forest 检测离群点
model = IsolationForest(contamination=0.05, random_state=42)

# 仅选择数值特征进行离群点检测
numerical_data = encoded_data.select_dtypes(include=[np.number])
encoded_data['outlier'] = model.fit_predict(numerical_data)

# 提取离群点
outliers = encoded_data[encoded_data['outlier'] == -1]

# 输出离群点
print(f"检测到的离群点（编码后）：\n{outliers}\n")

# 删除离群点
cleaned_encoded_data = encoded_data[encoded_data['outlier'] != -1]
print("删除离群点后的编码数据集大小：", cleaned_encoded_data.shape)

# 通过索引在原始数据中删除离群点
outlier_indices = outliers.index
cleaned_data = data.drop(index=outlier_indices)

# 输出原始数据中的离群点
original_outliers = data.loc[outlier_indices]
print(f"原始数据中的离群点：\n{original_outliers}\n")






# for col in data.columns:
#     plt.figure(figsize=(10, 10))  # 调整figsize以放大扇形图
    
#     # 获取列的值和计数
#     values = data[col].value_counts()
    
#     # 为每个数值设置一种颜色
#     colors = sns.color_palette('coolwarm', len(values))
    
#     # 设置explode参数，使得较小的扇面分开
#     explode = [0.1 if val/values.sum() < 0.05 else 0 for val in values]  # 如果比例小于5%，则分开
    
#     # 确保 explode 列表的长度与 values 的长度一致
#     if len(explode) < len(values):
#         explode += [0] * (len(values) - len(explode))

#     # 绘制扇形图
#     values.plot.pie(
#         autopct='%1.1f%%',  # 显示百分比
#         startangle=90, 
#         colors=colors,
#         labels=None,  # 不显示标签
#         explode=explode  # 设置explode以分开小的扇面
#     )
    
#     # 添加标题
#     plt.title(f'{col} 分布', fontproperties=my_font)
    
#     # 隐藏y轴的标签
#     plt.ylabel('')
    
#     # 在右上角添加图例
#     plt.legend(
#         labels=values.index,  # 数值标签
#         loc='upper right',    # 图例位置
#         title=col,            # 图例标题
#         prop=my_font,         # 字体属性
#         bbox_to_anchor=(1.3, 1)  # 调整图例位置，放大图表时向右移动图例
#     )
    
#     # 显示图表
#     plt.show()



# # 可视化各列的分布
# for col in data.columns:
#     plt.figure(figsize=(10, 6))
#     sns.histplot(data[col], kde=True)
#     plt.title(f'{col} 分布', fontproperties=my_font)
#     # plt.show()

# # 设置画布大小
# plt.figure(figsize=(12, 8))

# # 绘制“子女在读情况”的分布图
# sns.histplot(data['子女在读情况'], kde=True)

# # 设置图表标题
# plt.title('子女在读情况 分布', fontproperties=my_font)

# # 设置x坐标轴的文字为竖向排列，并调整字体大小
# plt.xticks(rotation=90, ha='center', fontproperties=my_font, fontsize=8)
# plt.yticks(fontsize=12, fontproperties=my_font)

# # 自动调整布局，以适应所有内容
# plt.tight_layout()

# # 显示图表
# plt.show()



# # 数据的初步概览和可视化展示
# print("数据集的前几行：\n", data.head())
# print("数据集的描述性统计：\n", data.describe())
# print("数据集的列名：\n", data.columns)

#假设标签列名为 '助学贷款帮助程度'
label = '助学贷款帮助程度'

# 创建一个字典来存储所有列的编码映射
label_mappings = {}

# 对每个非数值列进行编码，并保存映射关系
for col in data.select_dtypes(include=['object']).columns:
    le = LabelEncoder()
    data[col] = le.fit_transform(data[col])
    label_mappings[col] = dict(zip(le.classes_, le.transform(le.classes_)))

# 打印每个列的编码映射
for col, mapping in label_mappings.items():
    print(f"列 '{col}' 的整数编码映射：")
    for original, encoded in mapping.items():
        print(f"  {original} -> {encoded}")
    print()

# 计算相关系数矩阵
correlation_matrix = data.corr()

# 获取与标签的相关系数
label_correlation = correlation_matrix[label].drop(label)  # 去掉标签自身的相关系数
label_correlation_sorted = label_correlation.abs().sort_values(ascending=False)

# 选择与标签相关系数最高的前5个特征
top_features = label_correlation_sorted.head(5).index.tolist()

# 可视化整个相关系数矩阵，并标出前5个特征
plt.figure(figsize=(12, 10))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt='.2f', cbar_kws={'label': '相关系数'})
plt.xticks(rotation=45, ha='right') 
plt.title('相关系数矩阵（前5个与标签相关的特征已标记）', fontproperties=my_font)

# 高亮显示前5个与标签相关的特征
for feature in top_features:
    plt.gca().add_patch(plt.Rectangle((correlation_matrix.columns.get_loc(feature), 
                                       correlation_matrix.index.get_loc(label)),
                                      1, 1, fill=False, edgecolor='yellow', lw=3))
plt.show()

import seaborn as sns
import matplotlib.pyplot as plt

# 标签的具体类型映射
label_mapping = {0: ' 一些帮助', 1: '很大帮助', 2: '没有帮助'}

# 使用标签的实际名称代替数字
data['助学贷款帮助程度_标签'] = data['助学贷款帮助程度'].map(label_mapping)

# 创建一个新的画布，合理设置子图的排列
fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(15, 10))

# 列表中的每个特征都会绘制一个密度图
for i, feature in enumerate(top_features):
    row = i // 3
    col = i % 3
    sns.kdeplot(data=data, x=feature, hue='助学贷款帮助程度_标签', ax=axes[row, col], fill=True, palette='coolwarm')
    axes[row, col].set_title(feature)

# 移除未使用的子图（如果有的话）
if len(top_features) < 6:
    fig.delaxes(axes[1, 2])

# 设置整体标题并显示图形
plt.suptitle('前5个特征的密度分布及其与标签的关系', y=1.02, fontproperties=my_font)
plt.tight_layout()
plt.show()

from tensorflow.keras.callbacks import LearningRateScheduler

# 定义学习率调度函数
def scheduler(epoch, lr):
    if epoch > 10:  # 在第10个epoch之后降低学习率
        return lr * 0.9  # 每个epoch将学习率减少10%
    return lr

# 使用前5个特征作为模型输入
X = data[top_features]
y = data[label]

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 标准化特征
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 构建简单的神经网络模型
model = Sequential([
    Dense(16, activation='relu', input_shape=(X_train_scaled.shape[1],)),
    Dense(8, activation='relu'),
    Dense(1, activation='sigmoid')
])

# 编译模型
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# 实例化学习率调度器
lr_scheduler = LearningRateScheduler(scheduler)

# 训练模型，添加学习率调度器
history = model.fit(X_train_scaled, y_train, epochs=100, batch_size=1, validation_split=0.2, callbacks=[lr_scheduler])

# 可视化训练过程中的损失和准确率
plt.figure(figsize=(12, 6))

plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='训练集损失')
plt.plot(history.history['val_loss'], label='验证集损失')
plt.title('训练过程中的损失', fontproperties=my_font)   
plt.xlabel('Epoch', fontproperties=my_font)
plt.ylabel('Loss', fontproperties=my_font)
plt.legend(prop=my_font)

plt.subplot(1, 2, 2)
plt.plot(history.history['accuracy'], label='训练集准确率')
plt.plot(history.history['val_accuracy'], label='验证集准确率')
plt.title('训练过程中的准确率', fontproperties=my_font)
plt.xlabel('Epoch', fontproperties=my_font)
plt.ylabel('Accuracy', fontproperties=my_font)
plt.legend(prop=my_font)

plt.show()


# 评估模型
y_pred = model.predict(X_test_scaled).round().astype(int)

# 将预测值和实际值映射为中文标签
y_test_mapped = pd.Series(y_test).map(label_mapping)
y_pred_mapped = pd.Series(y_pred.flatten()).map(label_mapping)

# 生成分类报告
report = classification_report(y_test_mapped, y_pred_mapped, output_dict=True, zero_division=1)

# 可视化分类报告
df_report = pd.DataFrame(report).transpose()
plt.figure(figsize=(10, 6))
sns.heatmap(df_report.iloc[:-1, :].T, annot=True, cmap='coolwarm')
plt.title('神经网络分类报告', fontproperties=my_font)
plt.show()

# 绘制混淆矩阵
conf_matrix = confusion_matrix(y_test_mapped, y_pred_mapped, labels=['没有帮助', '一些帮助', '很大帮助'])
plt.figure(figsize=(8, 6))
sns.heatmap(conf_matrix, annot=True, fmt="d", cmap='coolwarm', xticklabels=label_mapping.values(), yticklabels=label_mapping.values())
plt.title('神经网络混淆矩阵', fontproperties=my_font)
plt.xlabel('预测值', fontproperties=my_font)
plt.ylabel('真实值', fontproperties=my_font)
plt.show()

# 使用KNN分类器进行对比
from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train_scaled, y_train)

# 评估KNN模型
y_pred_knn = knn.predict(X_test_scaled)

# 将KNN预测值映射为中文标签
y_pred_knn_mapped = pd.Series(y_pred_knn).map(label_mapping)

# 生成KNN分类报告
report_knn = classification_report(y_test_mapped, y_pred_knn_mapped, output_dict=True, zero_division=1)

# 可视化KNN分类报告
df_report_knn = pd.DataFrame(report_knn).transpose()
plt.figure(figsize=(10, 6))
sns.heatmap(df_report_knn.iloc[:-1, :].T, annot=True, cmap='coolwarm')
plt.title('KNN分类报告', fontproperties=my_font)
plt.show()

# 绘制KNN混淆矩阵
conf_matrix_knn = confusion_matrix(y_test_mapped, y_pred_knn_mapped, labels=['没有帮助', '一些帮助', '很大帮助'])
plt.figure(figsize=(8, 6))
sns.heatmap(conf_matrix_knn, annot=True, fmt="d", cmap='coolwarm', xticklabels=label_mapping.values(), yticklabels=label_mapping.values())
plt.title('KNN混淆矩阵', fontproperties=my_font)
plt.xlabel('预测值', fontproperties=my_font)
plt.ylabel('真实值', fontproperties=my_font)
plt.show()

# 对比神经网络和KNN的性能

# 将两个模型的分类报告合并到一个数据框中
df_report_combined = pd.concat([df_report, df_report_knn], axis=1, keys=['神经网络', 'KNN'])

# 可视化对比分类报告
plt.figure(figsize=(14, 8))
sns.heatmap(df_report_combined.iloc[:-1, :].T, annot=True, cmap='coolwarm')
plt.title('神经网络与KNN分类报告对比', fontproperties=my_font)
plt.show()

# 绘制对比混淆矩阵
plt.figure(figsize=(14, 6))

plt.subplot(1, 2, 1)
sns.heatmap(conf_matrix, annot=True, fmt="d", cmap='coolwarm', xticklabels=label_mapping.values(), yticklabels=label_mapping.values())
plt.title('神经网络混淆矩阵', fontproperties=my_font)
plt.xlabel('预测值', fontproperties=my_font)
plt.ylabel('真实值', fontproperties=my_font)

plt.subplot(1, 2, 2)
sns.heatmap(conf_matrix_knn, annot=True, fmt="d", cmap='coolwarm', xticklabels=label_mapping.values(), yticklabels=label_mapping.values())
plt.title('KNN混淆矩阵', fontproperties=my_font)
plt.xlabel('预测值', fontproperties=my_font)
plt.ylabel('真实值', fontproperties=my_font)

plt.tight_layout()
plt.show()


